arkaos 4.0.2 → 4.1.1
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.
- package/README.md +2 -2
- package/VERSION +1 -1
- package/arka/SKILL.md +16 -22
- package/arka/skills/flow/SKILL.md +108 -234
- package/config/claude-agents/eduardo-copy.md +53 -0
- package/config/claude-agents/francisca-tech.md +52 -0
- package/config/claude-agents/marta-cqo.md +52 -0
- package/config/claude-agents/paulo-tech-lead.md +50 -0
- package/config/cognition/schedules.yaml +15 -0
- package/config/constitution.yaml +17 -21
- package/config/hooks/_lib/workflow-classifier.sh +1 -1
- package/config/hooks/post-tool-use.sh +52 -536
- package/config/hooks/pre-tool-use.sh +46 -280
- package/config/hooks/session-start.sh +8 -10
- package/config/hooks/stop.sh +44 -424
- package/config/hooks/user-prompt-submit.sh +55 -457
- package/core/__pycache__/__init__.cpython-314.pyc +0 -0
- package/core/cognition/__pycache__/dreaming.cpython-313.pyc +0 -0
- package/core/cognition/dreaming.py +5 -0
- package/core/cognition/scheduler/__pycache__/daemon.cpython-313.pyc +0 -0
- package/core/cognition/scheduler/daemon.py +10 -0
- package/core/forge/__init__.py +2 -0
- package/core/forge/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/forge/__pycache__/orchestrator.cpython-313.pyc +0 -0
- package/core/forge/__pycache__/persistence.cpython-313.pyc +0 -0
- package/core/forge/__pycache__/renderer.cpython-313.pyc +0 -0
- package/core/forge/__pycache__/runtime_dispatcher.cpython-313.pyc +0 -0
- package/core/forge/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/forge/orchestrator.py +50 -27
- package/core/forge/persistence.py +16 -0
- package/core/forge/renderer.py +16 -0
- package/core/forge/runtime_dispatcher.py +34 -19
- package/core/forge/schema.py +12 -0
- package/core/governance/__pycache__/__init__.cpython-312.pyc +0 -0
- package/core/governance/__pycache__/__init__.cpython-314.pyc +0 -0
- package/core/governance/__pycache__/closing_marker_check.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/closing_marker_check.cpython-314.pyc +0 -0
- package/core/governance/__pycache__/compliance_telemetry.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/constitution.cpython-312.pyc +0 -0
- package/core/governance/__pycache__/constitution.cpython-314.pyc +0 -0
- package/core/governance/__pycache__/evidence_checks.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/kb_cite_check.cpython-314.pyc +0 -0
- package/core/governance/__pycache__/meta_tag_check.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/meta_tag_check.cpython-314.pyc +0 -0
- package/core/governance/__pycache__/qg_verdict.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/review_workflow.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/skill_proposer.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/skill_proposer.cpython-314.pyc +0 -0
- package/core/governance/__pycache__/sycophancy_detector.cpython-314.pyc +0 -0
- package/core/governance/closing_marker_check.py +9 -5
- package/core/governance/compliance_telemetry.py +1 -1
- package/core/governance/compliance_telemetry_cli.py +1 -1
- package/core/governance/evidence_checks.py +421 -0
- package/core/governance/meta_tag_check.py +36 -0
- package/core/governance/qg_verdict.py +78 -0
- package/core/governance/review_workflow.py +34 -1
- package/core/governance/skill_proposer.py +2 -1
- package/core/hooks/__init__.py +12 -0
- package/core/hooks/__pycache__/__init__.cpython-312.pyc +0 -0
- package/core/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/__init__.cpython-314.pyc +0 -0
- package/core/hooks/__pycache__/_shared.cpython-312.pyc +0 -0
- package/core/hooks/__pycache__/_shared.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/_shared.cpython-314.pyc +0 -0
- package/core/hooks/__pycache__/post_tool_use.cpython-312.pyc +0 -0
- package/core/hooks/__pycache__/post_tool_use.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/pre_tool_use.cpython-312.pyc +0 -0
- package/core/hooks/__pycache__/pre_tool_use.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/pre_tool_use.cpython-314.pyc +0 -0
- package/core/hooks/__pycache__/stop.cpython-312.pyc +0 -0
- package/core/hooks/__pycache__/stop.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/stop.cpython-314.pyc +0 -0
- package/core/hooks/__pycache__/user_prompt_submit.cpython-312.pyc +0 -0
- package/core/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
- package/core/hooks/_shared.py +110 -0
- package/core/hooks/post_tool_use.py +689 -0
- package/core/hooks/pre_tool_use.py +267 -0
- package/core/hooks/stop.py +395 -0
- package/core/hooks/user_prompt_submit.py +600 -0
- package/core/jobs/__pycache__/__init__.cpython-312.pyc +0 -0
- package/core/jobs/__pycache__/__init__.cpython-314.pyc +0 -0
- package/core/jobs/__pycache__/auto_doc_worker.cpython-312.pyc +0 -0
- package/core/jobs/__pycache__/auto_doc_worker.cpython-314.pyc +0 -0
- package/core/jobs/__pycache__/manager.cpython-312.pyc +0 -0
- package/core/jobs/__pycache__/manager.cpython-314.pyc +0 -0
- package/core/knowledge/__pycache__/embedder.cpython-312.pyc +0 -0
- package/core/knowledge/__pycache__/embedder.cpython-313.pyc +0 -0
- package/core/knowledge/__pycache__/vector_store.cpython-312.pyc +0 -0
- package/core/knowledge/__pycache__/vector_store.cpython-313.pyc +0 -0
- package/core/knowledge/embedder.py +118 -11
- package/core/knowledge/vector_store.py +111 -14
- package/core/memory/__pycache__/session_store.cpython-314.pyc +0 -0
- package/core/obsidian/__pycache__/relator.cpython-313.pyc +0 -0
- package/core/obsidian/relator.py +18 -8
- package/core/runtime/__pycache__/__init__.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/__init__.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/base.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/base.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/base.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/capabilities_cli.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/capabilities_cli.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/claude_code.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/claude_code.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/claude_code.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/codex_cli.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/codex_cli.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/codex_cli.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/cost_governor.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/cost_governor.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/cursor.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/cursor.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/cursor.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/gemini_cli.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/gemini_cli.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/gemini_cli.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/llm_cost_telemetry.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/llm_provider.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/llm_provider.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/llm_provider.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/llm_retry.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/llm_retry.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/llm_retry.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/native_usage.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/native_usage.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/native_usage.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/ollama_provider.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/path_resolver.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/pricing.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/pricing.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/pricing.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/registry.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/registry.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/registry.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/squad_orchestrator.cpython-312.pyc +0 -0
- package/core/runtime/base.py +29 -0
- package/core/runtime/capabilities_cli.py +74 -0
- package/core/runtime/claude_code.py +8 -0
- package/core/runtime/codex_cli.py +205 -37
- package/core/runtime/cost_governor.py +184 -0
- package/core/runtime/cursor.py +8 -0
- package/core/runtime/gemini_cli.py +8 -0
- package/core/runtime/llm_provider.py +32 -13
- package/core/runtime/llm_retry.py +233 -0
- package/core/runtime/native_usage.py +186 -0
- package/core/runtime/pricing.py +9 -0
- package/core/runtime/registry.py +3 -0
- package/core/shared/__pycache__/__init__.cpython-314.pyc +0 -0
- package/core/shared/__pycache__/safe_session_id.cpython-314.pyc +0 -0
- package/core/shared/__pycache__/test_evidence.cpython-313-pytest-9.1.1.pyc +0 -0
- package/core/shared/__pycache__/test_evidence.cpython-313.pyc +0 -0
- package/core/shared/test_evidence.py +34 -0
- package/core/synapse/__pycache__/__init__.cpython-314.pyc +0 -0
- package/core/synapse/__pycache__/agent_experiences_layer.cpython-312.pyc +0 -0
- package/core/synapse/__pycache__/cache.cpython-314.pyc +0 -0
- package/core/synapse/__pycache__/engine.cpython-312.pyc +0 -0
- package/core/synapse/__pycache__/engine.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/engine.cpython-314.pyc +0 -0
- package/core/synapse/__pycache__/graph_context_layer.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/kb_cache.cpython-312.pyc +0 -0
- package/core/synapse/__pycache__/kb_cache.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/kb_cache.cpython-314.pyc +0 -0
- package/core/synapse/__pycache__/layers.cpython-312.pyc +0 -0
- package/core/synapse/__pycache__/layers.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/layers.cpython-314.pyc +0 -0
- package/core/synapse/engine.py +7 -0
- package/core/synapse/graph_context_layer.py +212 -0
- package/core/synapse/kb_cache.py +3 -0
- package/core/synapse/layers.py +104 -20
- package/core/workflow/__pycache__/__init__.cpython-314.pyc +0 -0
- package/core/workflow/__pycache__/enforcer.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/engine.cpython-314.pyc +0 -0
- package/core/workflow/__pycache__/flow_enforcer.cpython-312.pyc +0 -0
- package/core/workflow/__pycache__/flow_enforcer.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/flow_enforcer.cpython-314.pyc +0 -0
- package/core/workflow/__pycache__/gate_checkpoint.cpython-312.pyc +0 -0
- package/core/workflow/__pycache__/gate_checkpoint.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/gate_checkpoint.cpython-314.pyc +0 -0
- package/core/workflow/__pycache__/loader.cpython-314.pyc +0 -0
- package/core/workflow/__pycache__/marker_cache.cpython-314.pyc +0 -0
- package/core/workflow/__pycache__/research_gate.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/research_gate.cpython-314.pyc +0 -0
- package/core/workflow/__pycache__/rules_registry.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/schema.cpython-314.pyc +0 -0
- package/core/workflow/__pycache__/specialist_enforcer.cpython-312.pyc +0 -0
- package/core/workflow/__pycache__/specialist_enforcer.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/state.cpython-314.pyc +0 -0
- package/core/workflow/enforcer.py +5 -16
- package/core/workflow/flow_enforcer.py +36 -11
- package/core/workflow/gate_checkpoint.py +149 -0
- package/core/workflow/research_gate.py +15 -3
- package/core/workflow/rules_registry.py +137 -389
- package/core/workflow/specialist_enforcer.py +21 -4
- package/dashboard/app/pages/knowledge/index.vue +4 -1
- package/departments/ops/skills/n8n-flow/SKILL.md +13 -0
- package/departments/ops/skills/workflow-automate/SKILL.md +16 -0
- package/departments/quality/SKILL.md +45 -5
- package/departments/quality/agents/copy-director.yaml +3 -1
- package/departments/quality/agents/tech-director.yaml +3 -1
- package/installer/adapters/claude-code.js +46 -2
- package/installer/config-seed.js +39 -14
- package/installer/doctor.js +8 -0
- package/installer/graphify.js +153 -0
- package/installer/index.js +20 -0
- package/installer/init.js +11 -0
- package/installer/update.js +19 -0
- package/package.json +1 -1
- package/pyproject.toml +3 -1
- package/scripts/__pycache__/dashboard-api.cpython-313.pyc +0 -0
- package/scripts/__pycache__/knowledge-index.cpython-313.pyc +0 -0
- package/scripts/__pycache__/synapse-bridge.cpython-312.pyc +0 -0
- package/scripts/__pycache__/synapse-bridge.cpython-313.pyc +0 -0
- package/scripts/knowledge-index.py +8 -2
- package/scripts/synapse-bridge.py +102 -26
|
@@ -1,37 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# ============================================================================
|
|
3
|
-
# ArkaOS v2 — PreToolUse Hook (
|
|
3
|
+
# ArkaOS v2 — PreToolUse Hook (thin wrapper, PR-6 v4.1.0 hook hygiene)
|
|
4
4
|
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# (`[arka:routing]`, `[arka:trivial]`, or `[arka:phase:`) in its last
|
|
8
|
-
# 3 messages of the transcript.
|
|
5
|
+
# Delegates the ENTIRE event to ONE python process:
|
|
6
|
+
# python3 -m core.hooks.pre_tool_use
|
|
9
7
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
8
|
+
# The entrypoint preserves the full gate chain and behavior contract of the
|
|
9
|
+
# previous 5-8-process version:
|
|
10
|
+
# 1. KB-first gate — core/workflow/research_gate.py::evaluate_research_gate
|
|
11
|
+
# (nudge on first external research call, deny on the second)
|
|
12
|
+
# 2. Specialist-dispatch gate — core/workflow/specialist_enforcer.py
|
|
13
|
+
# 3. Fast allow for non-flow-gated tools
|
|
14
|
+
# 4. CostGovernor budget check (stdlib-only; runs even when PyYAML is
|
|
15
|
+
# missing from the ambient python3)
|
|
16
|
+
# 5. Evidence-flow gate — core/workflow/flow_enforcer.py
|
|
13
17
|
#
|
|
14
|
-
#
|
|
15
|
-
# Allow semantics: no stdout, exit 0.
|
|
18
|
+
# Allow semantics: no stdout, exit 0 (nudges/warnings on stderr).
|
|
16
19
|
# Deny semantics: hookSpecificOutput.permissionDecision=deny JSON on stdout,
|
|
17
|
-
# `[ARKA:ENFORCEMENT]
|
|
20
|
+
# `[ARKA:ENFORCEMENT]` / `[ARKA:KB-FIRST]` / `[ARKA:SPECIALIST]` on stderr,
|
|
21
|
+
# exit 2. Env bypasses (ARKA_BYPASS_FLOW, ARKA_BYPASS_KB_FIRST) and feature
|
|
22
|
+
# flags are honored inside the python modules. Timeout: 10s.
|
|
18
23
|
# ============================================================================
|
|
19
24
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
# ─── Extract fields (docs: session_id, transcript_path, cwd, tool_name)
|
|
23
|
-
TOOL_NAME=""
|
|
24
|
-
TRANSCRIPT_PATH=""
|
|
25
|
-
SESSION_ID=""
|
|
26
|
-
CWD=""
|
|
27
|
-
if command -v jq &>/dev/null; then
|
|
28
|
-
TOOL_NAME=$(echo "$input" | jq -r '.tool_name // ""' 2>/dev/null)
|
|
29
|
-
TRANSCRIPT_PATH=$(echo "$input" | jq -r '.transcript_path // ""' 2>/dev/null)
|
|
30
|
-
SESSION_ID=$(echo "$input" | jq -r '.session_id // ""' 2>/dev/null)
|
|
31
|
-
CWD=$(echo "$input" | jq -r '.cwd // ""' 2>/dev/null)
|
|
32
|
-
fi
|
|
33
|
-
|
|
34
|
-
# ─── Resolve ARKAOS_ROOT (same rules as user-prompt-submit.sh) ──────────
|
|
25
|
+
# ─── Resolve ARKAOS_ROOT (env → .repo-path → ~/.arkaos → portable) ──────
|
|
35
26
|
if [ -z "${ARKAOS_ROOT:-}" ]; then
|
|
36
27
|
if [ -f "$HOME/.arkaos/.repo-path" ]; then
|
|
37
28
|
ARKAOS_ROOT=$(cat "$HOME/.arkaos/.repo-path")
|
|
@@ -41,265 +32,40 @@ if [ -z "${ARKAOS_ROOT:-}" ]; then
|
|
|
41
32
|
ARKAOS_ROOT="${ARKA_OS:-$HOME/.claude/skills/arkaos}"
|
|
42
33
|
fi
|
|
43
34
|
fi
|
|
35
|
+
export ARKAOS_ROOT
|
|
44
36
|
|
|
45
|
-
# ─── Degrade gracefully
|
|
37
|
+
# ─── Degrade gracefully (fail open, same as before) ─────────────────────
|
|
46
38
|
if ! command -v python3 &>/dev/null; then
|
|
47
39
|
exit 0
|
|
48
40
|
fi
|
|
49
|
-
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if [ -f "$ARKAOS_ROOT/core/workflow/research_gate.py" ]; then
|
|
60
|
-
KB_DECISION_JSON=$(TOOL_NAME="$TOOL_NAME" \
|
|
61
|
-
SESSION_ID="$SESSION_ID" \
|
|
62
|
-
QUERY_HINT="$QUERY_HINT" \
|
|
63
|
-
ARKAOS_ROOT="$ARKAOS_ROOT" \
|
|
64
|
-
python3 - <<'PY' 2>/dev/null
|
|
65
|
-
import json
|
|
66
|
-
import os
|
|
67
|
-
import sys
|
|
68
|
-
|
|
69
|
-
sys.path.insert(0, os.environ["ARKAOS_ROOT"])
|
|
70
|
-
try:
|
|
71
|
-
from core.workflow.research_gate import evaluate_research_gate, record_telemetry
|
|
72
|
-
except Exception:
|
|
73
|
-
print(json.dumps({"allow": True, "nudge": False, "reason": "kb-gate-import-failed"}))
|
|
74
|
-
sys.exit(0)
|
|
75
|
-
|
|
76
|
-
decision = evaluate_research_gate(
|
|
77
|
-
tool_name=os.environ.get("TOOL_NAME", ""),
|
|
78
|
-
session_id=os.environ.get("SESSION_ID", ""),
|
|
79
|
-
query=os.environ.get("QUERY_HINT", ""),
|
|
80
|
-
)
|
|
81
|
-
try:
|
|
82
|
-
record_telemetry(
|
|
83
|
-
session_id=os.environ.get("SESSION_ID", ""),
|
|
84
|
-
tool=os.environ.get("TOOL_NAME", ""),
|
|
85
|
-
decision=decision,
|
|
86
|
-
)
|
|
87
|
-
except Exception:
|
|
88
|
-
pass
|
|
89
|
-
print(json.dumps({
|
|
90
|
-
"allow": decision.allow,
|
|
91
|
-
"nudge": decision.nudge,
|
|
92
|
-
"reason": decision.reason,
|
|
93
|
-
"stderr_msg": decision.to_stderr_message(),
|
|
94
|
-
}))
|
|
95
|
-
PY
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
if [ -n "$KB_DECISION_JSON" ]; then
|
|
99
|
-
KB_ALLOW=$(echo "$KB_DECISION_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('allow', True))" 2>/dev/null)
|
|
100
|
-
KB_NUDGE=$(echo "$KB_DECISION_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('nudge', False))" 2>/dev/null)
|
|
101
|
-
KB_STDERR=$(echo "$KB_DECISION_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('stderr_msg',''))" 2>/dev/null)
|
|
102
|
-
|
|
103
|
-
if [ "$KB_ALLOW" != "True" ] && [ "$KB_ALLOW" != "true" ]; then
|
|
104
|
-
echo "$KB_STDERR" >&2
|
|
105
|
-
STDERR_MSG="$KB_STDERR" python3 - <<'PY'
|
|
106
|
-
import json, os
|
|
107
|
-
print(json.dumps({"hookSpecificOutput": {
|
|
108
|
-
"hookEventName": "PreToolUse",
|
|
109
|
-
"permissionDecision": "deny",
|
|
110
|
-
"permissionDecisionReason": os.environ.get("STDERR_MSG", ""),
|
|
111
|
-
}}))
|
|
112
|
-
PY
|
|
113
|
-
exit 2
|
|
114
|
-
fi
|
|
115
|
-
|
|
116
|
-
# Nudge path: emit advisory to stderr, continue to flow-marker gate.
|
|
117
|
-
if [ "$KB_NUDGE" = "True" ] || [ "$KB_NUDGE" = "true" ]; then
|
|
118
|
-
[ -n "$KB_STDERR" ] && echo "$KB_STDERR" >&2
|
|
119
|
-
fi
|
|
41
|
+
# Self-root fallback: the wrapper ships next to its python entrypoint, so
|
|
42
|
+
# when ARKAOS_ROOT points at a pre-PR-6 install without core/hooks/, use
|
|
43
|
+
# the root this script lives in.
|
|
44
|
+
if [ ! -f "$ARKAOS_ROOT/core/hooks/pre_tool_use.py" ]; then
|
|
45
|
+
_SELF_ROOT="$(cd "$(dirname "$0")/../.." 2>/dev/null && pwd)"
|
|
46
|
+
if [ -n "$_SELF_ROOT" ] && [ -f "$_SELF_ROOT/core/hooks/pre_tool_use.py" ]; then
|
|
47
|
+
ARKAOS_ROOT="$_SELF_ROOT"
|
|
48
|
+
export ARKAOS_ROOT
|
|
49
|
+
else
|
|
50
|
+
exit 0
|
|
120
51
|
fi
|
|
121
52
|
fi
|
|
122
53
|
|
|
123
|
-
# ───
|
|
124
|
-
#
|
|
125
|
-
#
|
|
126
|
-
#
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
CWD="$CWD" \
|
|
139
|
-
TOOL_INPUT_JSON="$TOOL_INPUT_JSON_SP" \
|
|
140
|
-
ARKAOS_ROOT="$ARKAOS_ROOT" \
|
|
141
|
-
python3 - <<'PY' 2>/dev/null
|
|
142
|
-
import json
|
|
143
|
-
import os
|
|
144
|
-
import sys
|
|
145
|
-
|
|
146
|
-
sys.path.insert(0, os.environ["ARKAOS_ROOT"])
|
|
147
|
-
try:
|
|
148
|
-
from core.workflow.specialist_enforcer import evaluate, record_telemetry
|
|
149
|
-
except Exception:
|
|
150
|
-
print(json.dumps({"allow": True, "reason": "specialist-import-failed"}))
|
|
151
|
-
sys.exit(0)
|
|
152
|
-
|
|
153
|
-
try:
|
|
154
|
-
tool_input = json.loads(os.environ.get("TOOL_INPUT_JSON", "{}"))
|
|
155
|
-
except json.JSONDecodeError:
|
|
156
|
-
tool_input = {}
|
|
157
|
-
|
|
158
|
-
decision = evaluate(
|
|
159
|
-
tool_name=os.environ.get("TOOL_NAME", ""),
|
|
160
|
-
transcript_path=os.environ.get("TRANSCRIPT_PATH", ""),
|
|
161
|
-
session_id=os.environ.get("SESSION_ID", ""),
|
|
162
|
-
cwd=os.environ.get("CWD", ""),
|
|
163
|
-
tool_input=tool_input,
|
|
164
|
-
)
|
|
165
|
-
try:
|
|
166
|
-
record_telemetry(
|
|
167
|
-
session_id=os.environ.get("SESSION_ID", ""),
|
|
168
|
-
tool=os.environ.get("TOOL_NAME", ""),
|
|
169
|
-
decision=decision,
|
|
170
|
-
cwd=os.environ.get("CWD", ""),
|
|
171
|
-
target_file=str(tool_input.get("file_path", "")),
|
|
172
|
-
)
|
|
173
|
-
except Exception:
|
|
174
|
-
pass
|
|
175
|
-
print(json.dumps({
|
|
176
|
-
"allow": decision.allow,
|
|
177
|
-
"reason": decision.reason,
|
|
178
|
-
"stderr_msg": decision.to_stderr_message(),
|
|
179
|
-
}))
|
|
180
|
-
PY
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
if [ -n "$SP_DECISION_JSON" ]; then
|
|
184
|
-
SP_ALLOW=$(echo "$SP_DECISION_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('allow', True))" 2>/dev/null)
|
|
185
|
-
if [ "$SP_ALLOW" != "True" ] && [ "$SP_ALLOW" != "true" ]; then
|
|
186
|
-
SP_STDERR=$(echo "$SP_DECISION_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('stderr_msg',''))" 2>/dev/null)
|
|
187
|
-
echo "$SP_STDERR" >&2
|
|
188
|
-
STDERR_MSG="$SP_STDERR" python3 - <<'PY'
|
|
189
|
-
import json, os
|
|
190
|
-
print(json.dumps({"hookSpecificOutput": {
|
|
191
|
-
"hookEventName": "PreToolUse",
|
|
192
|
-
"permissionDecision": "deny",
|
|
193
|
-
"permissionDecisionReason": os.environ.get("STDERR_MSG", ""),
|
|
194
|
-
}}))
|
|
195
|
-
PY
|
|
196
|
-
exit 2
|
|
197
|
-
fi
|
|
198
|
-
fi
|
|
199
|
-
;;
|
|
200
|
-
esac
|
|
201
|
-
fi
|
|
202
|
-
|
|
203
|
-
# ─── Fast allow: not a flow-gated tool ──────────────────────────────────
|
|
204
|
-
# PR11 v2.33.0 expanded the gated set to include all EFFECT tools.
|
|
205
|
-
# Bash is special — handled per-command by the Python enforcer via
|
|
206
|
-
# bash_is_effect(). Skip fast-path for Bash so the enforcer can classify.
|
|
207
|
-
case "$TOOL_NAME" in
|
|
208
|
-
Write|Edit|MultiEdit|NotebookEdit|Task|Skill|Bash) ;;
|
|
209
|
-
*) exit 0 ;;
|
|
210
|
-
esac
|
|
211
|
-
|
|
212
|
-
if [ ! -f "$ARKAOS_ROOT/core/workflow/flow_enforcer.py" ]; then
|
|
213
|
-
exit 0
|
|
214
|
-
fi
|
|
215
|
-
|
|
216
|
-
# ─── Extract tool_input (needed for Bash command classification) ───────
|
|
217
|
-
TOOL_INPUT_JSON="{}"
|
|
218
|
-
if command -v jq &>/dev/null; then
|
|
219
|
-
TOOL_INPUT_JSON=$(echo "$input" | jq -c '.tool_input // {}' 2>/dev/null)
|
|
220
|
-
fi
|
|
221
|
-
|
|
222
|
-
# ─── Delegate to Python enforcer ────────────────────────────────────────
|
|
223
|
-
DECISION_JSON=$(TOOL_NAME="$TOOL_NAME" \
|
|
224
|
-
TRANSCRIPT_PATH="$TRANSCRIPT_PATH" \
|
|
225
|
-
SESSION_ID="$SESSION_ID" \
|
|
226
|
-
CWD="$CWD" \
|
|
227
|
-
TOOL_INPUT_JSON="$TOOL_INPUT_JSON" \
|
|
228
|
-
ARKAOS_ROOT="$ARKAOS_ROOT" \
|
|
229
|
-
python3 - <<'PY' 2>/dev/null
|
|
230
|
-
import json
|
|
231
|
-
import os
|
|
232
|
-
import sys
|
|
233
|
-
|
|
234
|
-
sys.path.insert(0, os.environ["ARKAOS_ROOT"])
|
|
235
|
-
try:
|
|
236
|
-
from core.workflow.flow_enforcer import evaluate, record_telemetry
|
|
237
|
-
except Exception:
|
|
238
|
-
print(json.dumps({"allow": True, "reason": "enforcer-import-failed"}))
|
|
239
|
-
sys.exit(0)
|
|
240
|
-
|
|
241
|
-
try:
|
|
242
|
-
tool_input = json.loads(os.environ.get("TOOL_INPUT_JSON", "{}"))
|
|
243
|
-
except json.JSONDecodeError:
|
|
244
|
-
tool_input = {}
|
|
245
|
-
|
|
246
|
-
decision = evaluate(
|
|
247
|
-
tool_name=os.environ.get("TOOL_NAME", ""),
|
|
248
|
-
transcript_path=os.environ.get("TRANSCRIPT_PATH", ""),
|
|
249
|
-
session_id=os.environ.get("SESSION_ID", ""),
|
|
250
|
-
cwd=os.environ.get("CWD", ""),
|
|
251
|
-
tool_input=tool_input,
|
|
252
|
-
)
|
|
253
|
-
try:
|
|
254
|
-
record_telemetry(
|
|
255
|
-
session_id=os.environ.get("SESSION_ID", ""),
|
|
256
|
-
tool=os.environ.get("TOOL_NAME", ""),
|
|
257
|
-
decision=decision,
|
|
258
|
-
cwd=os.environ.get("CWD", ""),
|
|
259
|
-
)
|
|
260
|
-
except Exception:
|
|
261
|
-
pass
|
|
262
|
-
print(json.dumps({
|
|
263
|
-
"allow": decision.allow,
|
|
264
|
-
"reason": decision.reason,
|
|
265
|
-
"stderr_msg": decision.to_stderr_message(),
|
|
266
|
-
}))
|
|
267
|
-
PY
|
|
268
|
-
)
|
|
269
|
-
|
|
270
|
-
if [ -z "$DECISION_JSON" ]; then
|
|
271
|
-
exit 0
|
|
272
|
-
fi
|
|
273
|
-
|
|
274
|
-
ALLOW=$(echo "$DECISION_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('allow', True))" 2>/dev/null)
|
|
275
|
-
|
|
276
|
-
if [ "$ALLOW" = "True" ] || [ "$ALLOW" = "true" ]; then
|
|
277
|
-
exit 0
|
|
54
|
+
# ─── Single python process; stdin/stdout/stderr/exit-code pass through ──
|
|
55
|
+
# Interpreter resolution: prefer the ArkaOS venv (has pyyaml/pydantic);
|
|
56
|
+
# otherwise probe — an unrelated project venv first on PATH would give a
|
|
57
|
+
# python3 without yaml and silently degrade every gate.
|
|
58
|
+
_PY=""
|
|
59
|
+
for _cand in "$HOME/.arkaos/venv/bin/python3" "$HOME/.arkaos/.venv/bin/python3"; do
|
|
60
|
+
if [ -x "$_cand" ]; then _PY="$_cand"; break; fi
|
|
61
|
+
done
|
|
62
|
+
if [ -z "$_PY" ]; then
|
|
63
|
+
_PY="python3"
|
|
64
|
+
if ! "$_PY" -c "import yaml" 2>/dev/null; then
|
|
65
|
+
for _cand in /opt/homebrew/bin/python3 /usr/local/bin/python3 /usr/bin/python3; do
|
|
66
|
+
if [ -x "$_cand" ] && "$_cand" -c "import yaml" 2>/dev/null; then _PY="$_cand"; break; fi
|
|
67
|
+
done
|
|
68
|
+
fi
|
|
278
69
|
fi
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
REASON=$(echo "$DECISION_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('reason',''))" 2>/dev/null)
|
|
282
|
-
STDERR_MSG=$(echo "$DECISION_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('stderr_msg',''))" 2>/dev/null)
|
|
283
|
-
|
|
284
|
-
# Emit stderr (visible to the model per Claude Code hook spec) ─────────
|
|
285
|
-
echo "$STDERR_MSG" >&2
|
|
286
|
-
|
|
287
|
-
# Emit structured deny JSON (preferred path when runtime understands it).
|
|
288
|
-
# STDERR_MSG is passed via env var and read inside a single-quoted heredoc
|
|
289
|
-
# so no shell interpolation occurs inside the Python source — this closes
|
|
290
|
-
# the command-injection surface flagged by Francisca's tech review.
|
|
291
|
-
STDERR_MSG="$STDERR_MSG" python3 - <<'PY'
|
|
292
|
-
import json
|
|
293
|
-
import os
|
|
294
|
-
|
|
295
|
-
out = {
|
|
296
|
-
"hookSpecificOutput": {
|
|
297
|
-
"hookEventName": "PreToolUse",
|
|
298
|
-
"permissionDecision": "deny",
|
|
299
|
-
"permissionDecisionReason": os.environ.get("STDERR_MSG", ""),
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
print(json.dumps(out))
|
|
303
|
-
PY
|
|
304
|
-
|
|
305
|
-
exit 2
|
|
70
|
+
PYTHONPATH="$ARKAOS_ROOT${PYTHONPATH:+:$PYTHONPATH}" \
|
|
71
|
+
exec "$_PY" -m core.hooks.pre_tool_use
|
|
@@ -89,16 +89,14 @@ MSG+="ArkaOS v${VERSION} | 65 agents | 17 departments | 244+ skills"
|
|
|
89
89
|
[ -n "$_FORGE_LINE" ] && MSG+="\\n${_FORGE_LINE}"
|
|
90
90
|
MSG+="${DRIFT}"
|
|
91
91
|
|
|
92
|
-
# ───
|
|
93
|
-
MSG+="\\n\\n[ARKA:
|
|
94
|
-
MSG+="\\n
|
|
95
|
-
MSG+="\\n
|
|
96
|
-
MSG+="\\n
|
|
97
|
-
MSG+="\\n
|
|
98
|
-
MSG+="\\
|
|
99
|
-
MSG+="\\
|
|
100
|
-
MSG+="\\nEmit [arka:phase:N] before each step. Bypass ONLY via [arka:trivial] <reason> for single-file edits under 10 lines."
|
|
101
|
-
MSG+="\\nNo runtime setting, no user convenience, no task type can opt out. This contract overrides generic-assistant defaults."
|
|
92
|
+
# ─── Evidence Flow Contract (top-of-session, highest priority) ──────────
|
|
93
|
+
MSG+="\\n\\n[ARKA:EVIDENCE-FLOW] NON-NEGOTIABLE. Every non-trivial request runs the 4-gate evidence flow (constitution rule evidence-flow; source arka/skills/flow/SKILL.md):"
|
|
94
|
+
MSG+="\\n G1 CONTEXT ([arka:routing] <dept> -> <lead> + KB/graph grounding, cite or declare gap)"
|
|
95
|
+
MSG+="\\n G2 PLAN (short plan -> EXPLICIT user approval; silence != approval)"
|
|
96
|
+
MSG+="\\n G3 EXECUTE (closes only with real test run on record: command + exit 0)"
|
|
97
|
+
MSG+="\\n G4 REVIEW (executable checks: lint/type/coverage/security/spell -> honest summary)"
|
|
98
|
+
MSG+="\\nEmit [arka:gate:N] at each gate start. Gates pass on evidence, never on narration."
|
|
99
|
+
MSG+="\\nBypass ONLY via [arka:trivial] <reason> for single-file edits under 10 lines."
|
|
102
100
|
|
|
103
101
|
# ─── Transparency tag contract (PR12 v2.34.0) ───────────────────────────
|
|
104
102
|
MSG+="\\n\\n[ARKA:META-TAG] Every substantive response ends with a single line:"
|