skillware 0.3.6__tar.gz → 0.3.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {skillware-0.3.6/skillware.egg-info → skillware-0.3.7}/PKG-INFO +1 -1
- {skillware-0.3.6 → skillware-0.3.7}/pyproject.toml +1 -1
- {skillware-0.3.6 → skillware-0.3.7}/skills/finance/wallet_screening/card.json +4 -4
- {skillware-0.3.6 → skillware-0.3.7}/skills/finance/wallet_screening/instructions.md +5 -5
- skillware-0.3.7/skills/wellness/mental_coach/__init__.py +3 -0
- skillware-0.3.7/skills/wellness/mental_coach/card.json +25 -0
- skillware-0.3.7/skills/wellness/mental_coach/constraints.py +93 -0
- skillware-0.3.7/skills/wellness/mental_coach/crisis_gate.py +175 -0
- skillware-0.3.7/skills/wellness/mental_coach/instructions.md +37 -0
- skillware-0.3.7/skills/wellness/mental_coach/kb/corpus.json +214 -0
- skillware-0.3.7/skills/wellness/mental_coach/kb/crisis_resources.json +219 -0
- skillware-0.3.7/skills/wellness/mental_coach/kb/hard_constraints.yaml +86 -0
- skillware-0.3.7/skills/wellness/mental_coach/kb_provider.py +101 -0
- skillware-0.3.7/skills/wellness/mental_coach/manifest.yaml +66 -0
- skillware-0.3.7/skills/wellness/mental_coach/resources.py +71 -0
- skillware-0.3.7/skills/wellness/mental_coach/skill.py +396 -0
- skillware-0.3.7/skills/wellness/mental_coach/test_skill.py +112 -0
- skillware-0.3.7/skillware/core/__init__.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7/skillware.egg-info}/PKG-INFO +1 -1
- {skillware-0.3.6 → skillware-0.3.7}/skillware.egg-info/SOURCES.txt +14 -1
- skillware-0.3.6/tests/test_mica_module.py +0 -66
- {skillware-0.3.6 → skillware-0.3.7}/LICENSE +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/MANIFEST.in +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/README.md +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/setup.cfg +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/__init__.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/compliance/__init__.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/compliance/mica_module/__init__.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/compliance/mica_module/card.json +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/compliance/mica_module/instructions.md +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/compliance/mica_module/manifest.yaml +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/compliance/mica_module/mica_corpus.json +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/compliance/mica_module/skill.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/compliance/mica_module/test_skill.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/compliance/pii_masker/__init__.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/compliance/pii_masker/card.json +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/compliance/pii_masker/instructions.md +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/compliance/pii_masker/manifest.yaml +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/compliance/pii_masker/skill.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/compliance/pii_masker/test_skill.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/compliance/tos_evaluator/__init__.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/compliance/tos_evaluator/card.json +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/compliance/tos_evaluator/instructions.md +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/compliance/tos_evaluator/manifest.yaml +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/compliance/tos_evaluator/skill.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/compliance/tos_evaluator/test_skill.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/data_engineering/__init__.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/data_engineering/novelty_extractor/__init__.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/data_engineering/novelty_extractor/card.json +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/data_engineering/novelty_extractor/instructions.md +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/data_engineering/novelty_extractor/manifest.yaml +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/data_engineering/novelty_extractor/skill.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/data_engineering/novelty_extractor/test_skill.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/data_engineering/synthetic_generator/__init__.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/data_engineering/synthetic_generator/card.json +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/data_engineering/synthetic_generator/instructions.md +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/data_engineering/synthetic_generator/manifest.yaml +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/data_engineering/synthetic_generator/skill.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/data_engineering/synthetic_generator/test_skill.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/defi/__init__.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/defi/evm_tx_handler/__init__.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/defi/evm_tx_handler/abis.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/defi/evm_tx_handler/card.json +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/defi/evm_tx_handler/config.yaml.example +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/defi/evm_tx_handler/data/addressbook.yaml +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/defi/evm_tx_handler/data/chains.yaml +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/defi/evm_tx_handler/data/tokens.yaml +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/defi/evm_tx_handler/instructions.md +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/defi/evm_tx_handler/manifest.yaml +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/defi/evm_tx_handler/skill.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/defi/evm_tx_handler/test_skill.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/dev_tools/__init__.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/dev_tools/issue_resolver/__init__.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/dev_tools/issue_resolver/card.json +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/dev_tools/issue_resolver/instructions.md +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/dev_tools/issue_resolver/manifest.yaml +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/dev_tools/issue_resolver/skill.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/dev_tools/issue_resolver/test_skill.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/dev_tools/issue_resolver/workflow.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/finance/__init__.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/finance/wallet_screening/__init__.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/finance/wallet_screening/data/entities.ftm.json +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/finance/wallet_screening/data/malicious_scs_2025.json +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/finance/wallet_screening/data/normalized_fbi_lazarus_20250722_180019.json +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/finance/wallet_screening/data/normalized_israel_nbctf_20250722_180019.json +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/finance/wallet_screening/data/normalized_uniswap_trm.json +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/finance/wallet_screening/maintenance/normalization_tool.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/finance/wallet_screening/maintenance/normalize_uniswap_trm.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/finance/wallet_screening/manifest.yaml +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/finance/wallet_screening/skill.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/finance/wallet_screening/test_skill.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/office/__init__.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/office/pdf_form_filler/__init__.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/office/pdf_form_filler/card.json +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/office/pdf_form_filler/instructions.md +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/office/pdf_form_filler/manifest.yaml +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/office/pdf_form_filler/skill.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/office/pdf_form_filler/test_skill.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/office/pdf_form_filler/utils.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/optimization/__init__.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/optimization/prompt_rewriter/__init__.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/optimization/prompt_rewriter/card.json +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/optimization/prompt_rewriter/instructions.md +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/optimization/prompt_rewriter/manifest.yaml +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/optimization/prompt_rewriter/skill.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skills/optimization/prompt_rewriter/test_skill.py +0 -0
- {skillware-0.3.6/skillware → skillware-0.3.7/skills/wellness}/__init__.py +0 -0
- {skillware-0.3.6/skillware/core → skillware-0.3.7/skillware}/__init__.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skillware/__main__.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skillware/cli.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skillware/core/base_skill.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skillware/core/env.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skillware/core/loader.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skillware/version_policy.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skillware.egg-info/dependency_links.txt +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skillware.egg-info/entry_points.txt +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skillware.egg-info/requires.txt +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/skillware.egg-info/top_level.txt +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/tests/test_cli.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/tests/test_loader.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/tests/test_skill_issuer.py +0 -0
- {skillware-0.3.6 → skillware-0.3.7}/tests/test_version_policy.py +0 -0
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
"ui_schema": {
|
|
13
13
|
"type": "card",
|
|
14
14
|
"fields": [
|
|
15
|
-
{"key": "
|
|
16
|
-
{"key": "summary.
|
|
17
|
-
{"key": "summary.
|
|
18
|
-
{"key": "
|
|
15
|
+
{"key": "metadata.wallet_address", "label": "Address"},
|
|
16
|
+
{"key": "summary.sanctioned_entity_match", "label": "Sanctioned", "type": "status_badge"},
|
|
17
|
+
{"key": "summary.malicious_interaction_count", "label": "Risky Interactions", "type": "count"},
|
|
18
|
+
{"key": "financial_analysis.pnl_eth", "label": "PnL (ETH)", "type": "currency"}
|
|
19
19
|
]
|
|
20
20
|
}
|
|
21
21
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Wallet Screening Skill Instructions
|
|
2
2
|
|
|
3
|
-
You are equipped with the `wallet_screening` skill. This tool allows you to perform due diligence on Ethereum addresses.
|
|
3
|
+
You are equipped with the `finance/wallet_screening` skill. This tool allows you to perform due diligence on Ethereum addresses.
|
|
4
4
|
|
|
5
5
|
## When to use
|
|
6
6
|
Use this skill when the user:
|
|
@@ -13,10 +13,10 @@ Use this skill when the user:
|
|
|
13
13
|
The tool returns a JSON object. You should summarize this for the user in a professional, "Compliance Officer" tone.
|
|
14
14
|
|
|
15
15
|
### Key Fields to Check:
|
|
16
|
-
1. **`summary.
|
|
17
|
-
2. **`summary.
|
|
18
|
-
3. **`
|
|
19
|
-
4. **`
|
|
16
|
+
1. **`summary.sanctioned_entity_match` (Boolean)**: If `true`, this is CRITICAL. Report the `risk_details.sanctions_hits` immediately.
|
|
17
|
+
2. **`summary.malicious_interaction_count` (Integer)**: If > 0, the wallet has touched bad actors. List the `risk_details.malicious_interactions`.
|
|
18
|
+
3. **`financial_analysis.pnl_eth` / `pnl_usd`**: Profit and Loss. Useful for determining if it's a profitable trader or a victim.
|
|
19
|
+
4. **`network_analysis.top_10_counterparties`**: Who are they sending money to?
|
|
20
20
|
|
|
21
21
|
## Safety Protocol
|
|
22
22
|
* If a wallet is **Sanctioned**: severe warning. "⚠️ WARNING: This wallet appears on the following sanctions lists..."
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Mental Coach",
|
|
3
|
+
"description": "Wellness coaching guardrail with crisis triage and grounded KB retrieval.",
|
|
4
|
+
"issuer": {
|
|
5
|
+
"name": "Masa",
|
|
6
|
+
"email": "masa88keith@gmail.com",
|
|
7
|
+
"github": "mrmasa88",
|
|
8
|
+
"org": "AO"
|
|
9
|
+
},
|
|
10
|
+
"icon": "heart",
|
|
11
|
+
"color": "blue",
|
|
12
|
+
"ui_schema": {
|
|
13
|
+
"type": "card",
|
|
14
|
+
"fields": [
|
|
15
|
+
{
|
|
16
|
+
"key": "policy_status",
|
|
17
|
+
"label": "Policy Status"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"key": "scope",
|
|
21
|
+
"label": "Scope"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""Load and apply hard constraints from kb/hard_constraints.yaml."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
from functools import lru_cache
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
|
+
|
|
10
|
+
import yaml
|
|
11
|
+
|
|
12
|
+
_CONSTRAINTS_PATH = os.path.join(
|
|
13
|
+
os.path.dirname(__file__), "kb", "hard_constraints.yaml"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
CLINICAL_PATTERNS: Dict[str, List[str]] = {
|
|
17
|
+
"no_diagnosis": [
|
|
18
|
+
r"\bdiagnos(e|is|ing)\b",
|
|
19
|
+
r"\bdo i have (depression|anxiety|bipolar|adhd|ptsd|ocd)\b",
|
|
20
|
+
r"\bwhat (disorder|condition|illness) do i have\b",
|
|
21
|
+
r"\bam i (depressed|bipolar|schizophrenic)\b",
|
|
22
|
+
],
|
|
23
|
+
"no_medication_advice": [
|
|
24
|
+
r"\b(should i|can i) (take|stop|start) (my )?(medication|meds|antidepressant)\b",
|
|
25
|
+
r"\bwhat medication\b",
|
|
26
|
+
r"\bwhat dose\b",
|
|
27
|
+
r"\bprescribe\b",
|
|
28
|
+
r"\b(increase|decrease) my (dose|dosage)\b",
|
|
29
|
+
],
|
|
30
|
+
"no_clinical_interpretation": [
|
|
31
|
+
r"\binterpret my (lab|blood test|test results)\b",
|
|
32
|
+
r"\bwhat do my results mean\b",
|
|
33
|
+
r"\bclinical record\b",
|
|
34
|
+
],
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
INJECTION_PATTERNS = [
|
|
38
|
+
r"ignore (your|all|previous) (rules|instructions|constraints)",
|
|
39
|
+
r"disregard (your|the) (policy|safety|guidelines)",
|
|
40
|
+
r"pretend you are (a )?(doctor|therapist|psychiatrist)",
|
|
41
|
+
r"jailbreak",
|
|
42
|
+
r"bypass (your|the) (rules|safety|filter)",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@lru_cache(maxsize=1)
|
|
47
|
+
def load_constraints_config() -> Dict[str, Any]:
|
|
48
|
+
with open(_CONSTRAINTS_PATH, "r", encoding="utf-8") as handle:
|
|
49
|
+
data = yaml.safe_load(handle)
|
|
50
|
+
return data if isinstance(data, dict) else {}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def get_disclaimers(kind: str = "default") -> List[str]:
|
|
54
|
+
config = load_constraints_config()
|
|
55
|
+
disclaimers = config.get("disclaimers_required", {})
|
|
56
|
+
items = disclaimers.get(kind, disclaimers.get("default", []))
|
|
57
|
+
return list(items) if isinstance(items, list) else []
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def detect_clinical_violation(text: str) -> Optional[str]:
|
|
61
|
+
normalized = text.lower()
|
|
62
|
+
for constraint_id, patterns in CLINICAL_PATTERNS.items():
|
|
63
|
+
for pattern in patterns:
|
|
64
|
+
if re.search(pattern, normalized):
|
|
65
|
+
return constraint_id
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def detect_injection_attempt(text: str) -> bool:
|
|
70
|
+
normalized = text.lower()
|
|
71
|
+
return any(re.search(pattern, normalized) for pattern in INJECTION_PATTERNS)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def get_playbook_entry(decision: str) -> Dict[str, Any]:
|
|
75
|
+
config = load_constraints_config()
|
|
76
|
+
playbook = config.get("escalation_playbook", {})
|
|
77
|
+
key = {
|
|
78
|
+
"ESCALATE": "on_escalate",
|
|
79
|
+
"CAUTION": "on_caution",
|
|
80
|
+
"OK": "on_ok",
|
|
81
|
+
}.get(decision, "on_ok")
|
|
82
|
+
entry = playbook.get(key, {})
|
|
83
|
+
return entry if isinstance(entry, dict) else {}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def list_hard_constraint_ids() -> List[str]:
|
|
87
|
+
config = load_constraints_config()
|
|
88
|
+
items = config.get("hard_constraints", [])
|
|
89
|
+
ids: List[str] = []
|
|
90
|
+
for item in items:
|
|
91
|
+
if isinstance(item, dict) and item.get("id"):
|
|
92
|
+
ids.append(str(item["id"]))
|
|
93
|
+
return ids
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"""Deterministic crisis gate — runs before retrieval, no LLM dependency."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
import unicodedata
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from typing import Dict, List, Sequence, Tuple
|
|
9
|
+
|
|
10
|
+
# Minimal conservative signal sets (intent-level, not exhaustive lexicons).
|
|
11
|
+
CRISIS_SIGNALS: Dict[str, Sequence[str]] = {
|
|
12
|
+
"suicidal_ideation": (
|
|
13
|
+
"kill myself",
|
|
14
|
+
"end my life",
|
|
15
|
+
"want to die",
|
|
16
|
+
"suicidal",
|
|
17
|
+
"not want to live",
|
|
18
|
+
"better off dead",
|
|
19
|
+
"end it all",
|
|
20
|
+
),
|
|
21
|
+
"self_harm": (
|
|
22
|
+
"hurt myself",
|
|
23
|
+
"harm myself",
|
|
24
|
+
"cut myself",
|
|
25
|
+
"self harm",
|
|
26
|
+
"self-harm",
|
|
27
|
+
),
|
|
28
|
+
"harm_to_others": (
|
|
29
|
+
"hurt someone",
|
|
30
|
+
"kill him",
|
|
31
|
+
"kill her",
|
|
32
|
+
"kill them",
|
|
33
|
+
"harm others",
|
|
34
|
+
"going to hurt",
|
|
35
|
+
),
|
|
36
|
+
"abuse_or_violence": (
|
|
37
|
+
"being abused",
|
|
38
|
+
"he hits me",
|
|
39
|
+
"she hits me",
|
|
40
|
+
"sexually assaulted",
|
|
41
|
+
"domestic violence",
|
|
42
|
+
"partner hurts me",
|
|
43
|
+
),
|
|
44
|
+
"acute_medical_emergency": (
|
|
45
|
+
"can't breathe",
|
|
46
|
+
"cannot breathe",
|
|
47
|
+
"chest pain",
|
|
48
|
+
"overdose",
|
|
49
|
+
"heart attack",
|
|
50
|
+
"having a stroke",
|
|
51
|
+
),
|
|
52
|
+
"severe_acute_distress": (
|
|
53
|
+
"can't cope",
|
|
54
|
+
"cannot cope",
|
|
55
|
+
"falling apart",
|
|
56
|
+
"panic attack",
|
|
57
|
+
"overwhelmed can't function",
|
|
58
|
+
"completely overwhelmed",
|
|
59
|
+
),
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
CATEGORY_ACTION = {
|
|
63
|
+
"suicidal_ideation": "ESCALATE",
|
|
64
|
+
"self_harm": "ESCALATE",
|
|
65
|
+
"harm_to_others": "ESCALATE",
|
|
66
|
+
"abuse_or_violence": "ESCALATE",
|
|
67
|
+
"acute_medical_emergency": "ESCALATE",
|
|
68
|
+
"severe_acute_distress": "CAUTION",
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
NEGATION_PREFIXES = (
|
|
72
|
+
"don't ",
|
|
73
|
+
"do not ",
|
|
74
|
+
"never ",
|
|
75
|
+
"not going to ",
|
|
76
|
+
"won't ",
|
|
77
|
+
"wouldn't ",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
CJK_RE = re.compile(r"[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uac00-\ud7af]")
|
|
81
|
+
NON_ASCII_RE = re.compile(r"[^\x00-\x7F]")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass
|
|
85
|
+
class LanguageAssessment:
|
|
86
|
+
is_english: bool
|
|
87
|
+
non_english: bool
|
|
88
|
+
reason: str = ""
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass
|
|
92
|
+
class GateResult:
|
|
93
|
+
policy_status: str
|
|
94
|
+
crisis_categories: List[str] = field(default_factory=list)
|
|
95
|
+
ambiguous: bool = False
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def normalize_text(text: str) -> str:
|
|
99
|
+
normalized = unicodedata.normalize("NFKC", text or "")
|
|
100
|
+
normalized = re.sub(r"\s+", " ", normalized).strip().lower()
|
|
101
|
+
return normalized
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def assess_language(text: str) -> LanguageAssessment:
|
|
105
|
+
if not text.strip():
|
|
106
|
+
return LanguageAssessment(is_english=True, non_english=False)
|
|
107
|
+
|
|
108
|
+
if CJK_RE.search(text):
|
|
109
|
+
return LanguageAssessment(
|
|
110
|
+
is_english=False,
|
|
111
|
+
non_english=True,
|
|
112
|
+
reason="CJK characters detected; v0.1 is EN-first.",
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
non_ascii = len(NON_ASCII_RE.findall(text))
|
|
116
|
+
if non_ascii >= 8 or (len(text) > 0 and non_ascii / len(text) > 0.15):
|
|
117
|
+
return LanguageAssessment(
|
|
118
|
+
is_english=False,
|
|
119
|
+
non_english=True,
|
|
120
|
+
reason="High non-ASCII ratio; v0.1 is EN-first.",
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
return LanguageAssessment(is_english=True, non_english=False)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _has_negation_before(text: str, index: int) -> bool:
|
|
127
|
+
window = text[max(0, index - 48) : index]
|
|
128
|
+
return any(prefix in window for prefix in NEGATION_PREFIXES)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _match_signals(normalized: str) -> Tuple[List[str], bool]:
|
|
132
|
+
matched: List[str] = []
|
|
133
|
+
ambiguous = False
|
|
134
|
+
for category, signals in CRISIS_SIGNALS.items():
|
|
135
|
+
for signal in signals:
|
|
136
|
+
start = 0
|
|
137
|
+
while True:
|
|
138
|
+
idx = normalized.find(signal, start)
|
|
139
|
+
if idx < 0:
|
|
140
|
+
break
|
|
141
|
+
if _has_negation_before(normalized, idx):
|
|
142
|
+
ambiguous = True
|
|
143
|
+
else:
|
|
144
|
+
matched.append(category)
|
|
145
|
+
start = idx + len(signal)
|
|
146
|
+
# Preserve order, dedupe
|
|
147
|
+
seen = set()
|
|
148
|
+
ordered: List[str] = []
|
|
149
|
+
for cat in matched:
|
|
150
|
+
if cat not in seen:
|
|
151
|
+
ordered.append(cat)
|
|
152
|
+
seen.add(cat)
|
|
153
|
+
return ordered, ambiguous
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _resolve_policy(categories: Sequence[str], ambiguous: bool) -> str:
|
|
157
|
+
if categories:
|
|
158
|
+
if any(CATEGORY_ACTION.get(c) == "ESCALATE" for c in categories):
|
|
159
|
+
return "ESCALATE"
|
|
160
|
+
if any(CATEGORY_ACTION.get(c) == "CAUTION" for c in categories):
|
|
161
|
+
return "CAUTION"
|
|
162
|
+
if ambiguous:
|
|
163
|
+
return "CAUTION"
|
|
164
|
+
return "OK"
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def evaluate_crisis_gate(user_prompt: str) -> GateResult:
|
|
168
|
+
normalized = normalize_text(user_prompt)
|
|
169
|
+
categories, ambiguous = _match_signals(normalized)
|
|
170
|
+
policy_status = _resolve_policy(categories, ambiguous)
|
|
171
|
+
return GateResult(
|
|
172
|
+
policy_status=policy_status,
|
|
173
|
+
crisis_categories=categories,
|
|
174
|
+
ambiguous=ambiguous and policy_status != "ESCALATE",
|
|
175
|
+
)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Operational Instructions: Mental Coach
|
|
2
|
+
|
|
3
|
+
You are an agent equipped with the `wellness/mental_coach` skill.
|
|
4
|
+
|
|
5
|
+
## When to use this skill
|
|
6
|
+
|
|
7
|
+
- Before responding to wellness, stress, coping, or psychoeducation requests.
|
|
8
|
+
- When you need deterministic crisis triage and non-clinical scope enforcement.
|
|
9
|
+
- When you must ground coaching guidance in cited KB chunks with required disclaimers.
|
|
10
|
+
|
|
11
|
+
## What this skill does
|
|
12
|
+
|
|
13
|
+
1. Runs a deterministic crisis gate first (no LLM) and escalates on danger signals.
|
|
14
|
+
2. Blocks clinical requests (diagnosis, medication advice, clinical interpretation).
|
|
15
|
+
3. Retrieves grounded coaching chunks from the embedded public KB when safe to coach.
|
|
16
|
+
4. Optionally runs a scope evaluator (Gemini) when `run_evaluator` is enabled.
|
|
17
|
+
|
|
18
|
+
## How to interpret output
|
|
19
|
+
|
|
20
|
+
- `policy_status: ESCALATE` — stop coaching; use `final_context_for_agent` and resources only.
|
|
21
|
+
- `policy_status: BLOCKED` — decline the clinical request; offer non-clinical alternatives.
|
|
22
|
+
- `policy_status: CAUTION` — proceed gently; include disclaimers and consider resources.
|
|
23
|
+
- `policy_status: OK` — coaching path; follow retrieved citations exactly.
|
|
24
|
+
|
|
25
|
+
Always surface `disclaimers_required` verbatim in the user-facing reply.
|
|
26
|
+
|
|
27
|
+
## Limitations
|
|
28
|
+
|
|
29
|
+
- Supportive coaching and psychoeducation only; not emergency services or licensed care.
|
|
30
|
+
- v0.1 is English-first; non-English input is routed to CAUTION with resources.
|
|
31
|
+
- Optional evaluator requires `GOOGLE_API_KEY` when enabled.
|
|
32
|
+
|
|
33
|
+
## Example uses
|
|
34
|
+
|
|
35
|
+
- User: "I feel stressed at work and need coping strategies." -> coaching retrieval path.
|
|
36
|
+
- User: "Can you diagnose my anxiety?" -> blocked clinical path.
|
|
37
|
+
- User: "I want to hurt myself." -> crisis escalation path (no coaching content).
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_meta": {
|
|
3
|
+
"file": "corpus.json",
|
|
4
|
+
"skill": "wellness/mental_coach",
|
|
5
|
+
"purpose": "Public grounding KB with generic, publicly-citable coaching content.",
|
|
6
|
+
"version": "0.1-seed",
|
|
7
|
+
"language_scope": "EN (EU + North America launch markets)",
|
|
8
|
+
"content_policy": "All `text` fields are ORIGINAL PARAPHRASED SUMMARIES of public sources (no verbatim copying). `source_url` is provided for grounding and citation. Re-verify each release; some public pages change or note reduced update frequency.",
|
|
9
|
+
"verified_on": "2026-06-18",
|
|
10
|
+
"recheck_cadence": "Re-verify wording against source_url every release."
|
|
11
|
+
},
|
|
12
|
+
"chunks": [
|
|
13
|
+
{
|
|
14
|
+
"chunk_id": "scope-coaching-vs-therapy-0001",
|
|
15
|
+
"source_doc": "ICF Core Competencies (International Coaching Federation)",
|
|
16
|
+
"section": "Demonstrates ethical practice",
|
|
17
|
+
"jurisdiction": "GLOBAL",
|
|
18
|
+
"session_modes": [
|
|
19
|
+
"information"
|
|
20
|
+
],
|
|
21
|
+
"framework": "scope",
|
|
22
|
+
"tags": [
|
|
23
|
+
"scope",
|
|
24
|
+
"coaching",
|
|
25
|
+
"boundary",
|
|
26
|
+
"referral"
|
|
27
|
+
],
|
|
28
|
+
"text": "Coaching and psychoeducation are future-focused, growth-oriented forms of support for generally well-functioning people. They are distinct from psychotherapy and do not diagnose conditions or treat mental illness. Recognized coaching standards require keeping this distinction clear and referring people to qualified professionals when clinical support is needed.",
|
|
29
|
+
"safety": {
|
|
30
|
+
"suppress_in_crisis": true,
|
|
31
|
+
"requires_disclaimer": true
|
|
32
|
+
},
|
|
33
|
+
"source_url": "https://coachingfederation.org/credentialing/icf-core-competencies",
|
|
34
|
+
"visibility": "public",
|
|
35
|
+
"version": "0.1",
|
|
36
|
+
"updated": "2026-06-18"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"chunk_id": "self-care-not-cure-0002",
|
|
40
|
+
"source_doc": "NIMH - Caring for Your Mental Health",
|
|
41
|
+
"section": "Self-care",
|
|
42
|
+
"jurisdiction": "GLOBAL",
|
|
43
|
+
"session_modes": [
|
|
44
|
+
"information",
|
|
45
|
+
"coaching"
|
|
46
|
+
],
|
|
47
|
+
"framework": "scope",
|
|
48
|
+
"tags": [
|
|
49
|
+
"self-care",
|
|
50
|
+
"limits",
|
|
51
|
+
"treatment"
|
|
52
|
+
],
|
|
53
|
+
"text": "Self-care can help maintain wellbeing and manage mild symptoms, but it is not a cure for mental illness and does not replace professional treatment when that is needed.",
|
|
54
|
+
"safety": {
|
|
55
|
+
"suppress_in_crisis": true,
|
|
56
|
+
"requires_disclaimer": true
|
|
57
|
+
},
|
|
58
|
+
"source_url": "https://www.nimh.nih.gov/health/topics/caring-for-your-mental-health",
|
|
59
|
+
"visibility": "public",
|
|
60
|
+
"version": "0.1",
|
|
61
|
+
"updated": "2026-06-18"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"chunk_id": "when-to-seek-help-0003",
|
|
65
|
+
"source_doc": "NIMH - Caring for Your Mental Health",
|
|
66
|
+
"section": "When to seek professional help",
|
|
67
|
+
"jurisdiction": "GLOBAL",
|
|
68
|
+
"session_modes": [
|
|
69
|
+
"information"
|
|
70
|
+
],
|
|
71
|
+
"framework": "help-seeking",
|
|
72
|
+
"tags": [
|
|
73
|
+
"seek-help",
|
|
74
|
+
"professional",
|
|
75
|
+
"primary-care"
|
|
76
|
+
],
|
|
77
|
+
"text": "Consider seeking professional help if severe or distressing symptoms persist for two weeks or more. A useful first step is talking with a primary care provider, who can refer you to a qualified mental health professional.",
|
|
78
|
+
"safety": {
|
|
79
|
+
"suppress_in_crisis": true,
|
|
80
|
+
"requires_disclaimer": true
|
|
81
|
+
},
|
|
82
|
+
"source_url": "https://www.nimh.nih.gov/health/topics/caring-for-your-mental-health",
|
|
83
|
+
"visibility": "public",
|
|
84
|
+
"version": "0.1",
|
|
85
|
+
"updated": "2026-06-18"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"chunk_id": "urgent-help-uk-0004",
|
|
89
|
+
"source_doc": "NHS - Mental health",
|
|
90
|
+
"section": "Urgent help",
|
|
91
|
+
"jurisdiction": "UK",
|
|
92
|
+
"session_modes": [
|
|
93
|
+
"information"
|
|
94
|
+
],
|
|
95
|
+
"framework": "help-seeking",
|
|
96
|
+
"tags": [
|
|
97
|
+
"urgent",
|
|
98
|
+
"uk",
|
|
99
|
+
"nhs-111",
|
|
100
|
+
"emergency"
|
|
101
|
+
],
|
|
102
|
+
"text": "In England, urgent mental health support is available through NHS 111. If someone's life is at immediate risk, call 999 or go to A&E.",
|
|
103
|
+
"safety": {
|
|
104
|
+
"suppress_in_crisis": true,
|
|
105
|
+
"requires_disclaimer": true,
|
|
106
|
+
"notes": "Pairs with crisis_resources.json UK entries."
|
|
107
|
+
},
|
|
108
|
+
"source_url": "https://www.nhs.uk/mental-health/",
|
|
109
|
+
"visibility": "public",
|
|
110
|
+
"version": "0.1",
|
|
111
|
+
"updated": "2026-06-18"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"chunk_id": "mental-health-continuum-0005",
|
|
115
|
+
"source_doc": "WHO - Mental health fact sheet",
|
|
116
|
+
"section": "Concept",
|
|
117
|
+
"jurisdiction": "GLOBAL",
|
|
118
|
+
"session_modes": [
|
|
119
|
+
"information"
|
|
120
|
+
],
|
|
121
|
+
"framework": "psychoeducation",
|
|
122
|
+
"tags": [
|
|
123
|
+
"definition",
|
|
124
|
+
"continuum",
|
|
125
|
+
"who"
|
|
126
|
+
],
|
|
127
|
+
"text": "Mental health is more than the absence of illness. It exists on a continuum that differs between people and over time, shaped by individual, social, and structural factors, and many mental health conditions can be treated effectively.",
|
|
128
|
+
"safety": {
|
|
129
|
+
"suppress_in_crisis": true,
|
|
130
|
+
"requires_disclaimer": true
|
|
131
|
+
},
|
|
132
|
+
"source_url": "https://www.who.int/news-room/fact-sheets/detail/mental-health-strengthening-our-response",
|
|
133
|
+
"visibility": "public",
|
|
134
|
+
"version": "0.1",
|
|
135
|
+
"updated": "2026-06-18"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"chunk_id": "wellbeing-basics-0006",
|
|
139
|
+
"source_doc": "NIMH - Caring for Your Mental Health; NHS - Self-help",
|
|
140
|
+
"section": "Everyday wellbeing",
|
|
141
|
+
"jurisdiction": "GLOBAL",
|
|
142
|
+
"session_modes": [
|
|
143
|
+
"coaching",
|
|
144
|
+
"information"
|
|
145
|
+
],
|
|
146
|
+
"framework": "wellbeing",
|
|
147
|
+
"tags": [
|
|
148
|
+
"sleep",
|
|
149
|
+
"exercise",
|
|
150
|
+
"nutrition",
|
|
151
|
+
"connection"
|
|
152
|
+
],
|
|
153
|
+
"text": "Everyday practices that support wellbeing for many people include regular physical activity, balanced meals and staying hydrated, a consistent sleep routine, and staying socially connected. This is general guidance, not treatment.",
|
|
154
|
+
"safety": {
|
|
155
|
+
"suppress_in_crisis": true,
|
|
156
|
+
"requires_disclaimer": true
|
|
157
|
+
},
|
|
158
|
+
"source_url": "https://www.nimh.nih.gov/health/topics/caring-for-your-mental-health",
|
|
159
|
+
"visibility": "public",
|
|
160
|
+
"version": "0.1",
|
|
161
|
+
"updated": "2026-06-18"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"chunk_id": "mindfulness-general-0007",
|
|
165
|
+
"source_doc": "NHS - Mindfulness",
|
|
166
|
+
"section": "Mindfulness",
|
|
167
|
+
"jurisdiction": "GLOBAL",
|
|
168
|
+
"session_modes": [
|
|
169
|
+
"coaching",
|
|
170
|
+
"information"
|
|
171
|
+
],
|
|
172
|
+
"framework": "wellbeing",
|
|
173
|
+
"tags": [
|
|
174
|
+
"mindfulness",
|
|
175
|
+
"stress",
|
|
176
|
+
"present-moment"
|
|
177
|
+
],
|
|
178
|
+
"text": "Paying attention to the present moment can improve wellbeing for some people and may help with everyday stress. It does not suit everyone and is not a substitute for professional treatment.",
|
|
179
|
+
"safety": {
|
|
180
|
+
"suppress_in_crisis": true,
|
|
181
|
+
"requires_disclaimer": true
|
|
182
|
+
},
|
|
183
|
+
"source_url": "https://www.nhs.uk/mental-health/self-help/tips-and-support/mindfulness/",
|
|
184
|
+
"visibility": "public",
|
|
185
|
+
"version": "0.1",
|
|
186
|
+
"updated": "2026-06-18"
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"chunk_id": "service-disclaimer-0008",
|
|
190
|
+
"source_doc": "NIMH - Help for Mental Illnesses",
|
|
191
|
+
"section": "Informational use",
|
|
192
|
+
"jurisdiction": "GLOBAL",
|
|
193
|
+
"session_modes": [
|
|
194
|
+
"information",
|
|
195
|
+
"coaching"
|
|
196
|
+
],
|
|
197
|
+
"framework": "scope",
|
|
198
|
+
"tags": [
|
|
199
|
+
"disclaimer",
|
|
200
|
+
"informational",
|
|
201
|
+
"referral"
|
|
202
|
+
],
|
|
203
|
+
"text": "This service provides general support and information only. It does not diagnose conditions, provide treatment, or give personal medical advice. For concerns about your health, consult a qualified professional.",
|
|
204
|
+
"safety": {
|
|
205
|
+
"suppress_in_crisis": true,
|
|
206
|
+
"requires_disclaimer": true
|
|
207
|
+
},
|
|
208
|
+
"source_url": "https://www.nimh.nih.gov/health/find-help",
|
|
209
|
+
"visibility": "public",
|
|
210
|
+
"version": "0.1",
|
|
211
|
+
"updated": "2026-06-18"
|
|
212
|
+
}
|
|
213
|
+
]
|
|
214
|
+
}
|